﻿using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using NSun.Data;

namespace NSun.Data
{
    public abstract class BaseDbQuery<T> : DBQuery where T : class,IBaseEntity
    {
        internal static TableSchema Table { get; set; }        

        #region Construction

        static BaseDbQuery()
        {
            Table = DBQueryFactory.GetTableSchema(typeof(T));
        }


        internal BaseDbQuery(Database db)
            : base(db) { }

        internal BaseDbQuery(Database db, DBQueryFactory factory)
            : base(db, factory)
        {

        }

        #endregion

        //修改
        #region Create Command

        public SelectSqlSection<T> CreateQuery()
        {            
            var select = new SelectSqlSection<T>(Db, Table.TableName);
            select.Select(Table.QueryColumns.Values.ToArray());
            var tran = GetCurrentDbTransaction();
            if (null != tran)
            {
                select.SetTransaction(tran);
            }
            return select;
        }

        public SelectSqlSection<T> CreateQuery(DbTransaction tran)
        {
            var select = CreateQuery();
            select.SetTransaction(tran);
            return select;
        }

        public InsertSqlSection CreateInsert()
        {
            var insert = CreateInsert(Table.TableName);
            insert.IsAutoGenerated = Table.IsIdentyFieldAutoGenerated;
            var tran= GetCurrentDbTransaction();
            if (null != tran)
            {
                insert.SetTransaction(tran);                
            } 
            return insert;
        }

        public InsertSqlSection CreateInsert(DbTransaction tran)
        {
            Check.Require(tran != null);
            var insert = CreateInsert(Table.TableName, tran);
            insert.IsAutoGenerated = Table.IsIdentyFieldAutoGenerated;
            return insert;
        }

        public UpdateSqlSection<T> CreateUpdate()
        {
            UpdateSqlSection<T> up = new UpdateSqlSection<T>(Db, Table.TableName);
            var tran = GetCurrentDbTransaction();
            if (null != tran)
            {
                up.SetTransaction(tran);
            }
            return up;
        }

        public UpdateSqlSection<T> CreateUpdate(DbTransaction tran)
        {
            Check.Require(tran != null);
            UpdateSqlSection<T> up = new UpdateSqlSection<T>(Db, Table.TableName); 
            up.SetTransaction(tran);
            return up;
        }

        public DeleteSqlSection<T> CreateDelete()
        {
            //return CreateDelete(null);
            DeleteSqlSection<T> del = new DeleteSqlSection<T>(Db, Table.TableName);
            var tran = GetCurrentDbTransaction();
            if (null != tran)
            {
                del.SetTransaction(tran);
            }           
            return del;
        }

        public DeleteSqlSection<T> CreateDelete(DbTransaction tran)
        {
            Check.Require(tran != null);
            DeleteSqlSection<T> del = new DeleteSqlSection<T>(Db, Table.TableName);
            del.SetTransaction(tran);            
            return del;
        } 

        #endregion

        #region Cmd Method

        protected int InsertQueryColumn(InsertSqlSection insertsql, T tag)
        {
            InsertFieldBind(insertsql, tag); 
            if (IsUnitOfWork)
            {
                UnitOfWorkExecContext.TryAdd(insertsql, new DbQueryAndArgument() {DbQuery = this, Argument = tag});
                return -1;
            }

            if (Table.IsIdentyFieldAutoGenerated)
            {
                var id = ToExecuteReturnAutoIncrementId(insertsql, Table.IdentyColumn);
                ((PropertyInfo)Table.IdentyFieldMemberInfo).SetValue(tag,
                                                                Convert.ChangeType(id,
                                                                                   ((PropertyInfo)Table.IdentyFieldMemberInfo)
                                                                                       .PropertyType),
                                                                null);
                tag.SetIsPersistence(true);
                return id;
            }
            var res = ToExecute(insertsql);
            tag.SetIsPersistence(true);
            return res;
        }
         
        protected int UpdateQueryColumn(UpdateSqlSection updatesql, T tag)
        {
            //foreach (KeyValuePair<string, QueryColumn> field in UpdateFields.Fields)
            //{
            //    if (IdentyColumn.ToString() != field.Value.ToString())
            //        updatesql.AddColumn(field.Value, ReflectionUtils.GetFieldValue(tag, field.Key));
            //}
            //updatesql.Where(IdentyColumn == ReflectionUtils.GetFieldValue(tag, IdentyFieldMemberInfo));

            //foreach (var queryColumn in OptimisticLockingCache)
            //{
            //    updatesql.Where(queryColumn.Value == ReflectionUtils.GetFieldValue(tag, queryColumn.Key));
            //} 
            UpdateFieldBind(updatesql, tag);
            if (IsUnitOfWork)
            {
                UnitOfWorkExecContext.TryAdd(updatesql, new DbQueryAndArgument() { DbQuery = this, Argument = tag });
                return -1;
            }
            return ToExecute(updatesql);
        }

        private void InsertFieldBind(InsertSqlSection insertsql, T tag)
        {
            foreach (KeyValuePair<string, QueryColumn> field in Table.CreateFields.Fields)
            {
                KeyValuePair<string, QueryColumn> field1 = field;
                PropertyInfo pi = Table.ValuePropertys.FirstOrDefault(item => item.Name.ToLower() == field1.Key.ToLower());
                if (pi != null)
                {
                    object entityFieldValue = pi.GetValue(tag, null);

                    //if (!CommonUtils.IsEqual(entityFieldValue, CommonUtils.DefaultValue(pi.PropertyType)))
                    if (!CommonUtils.IsDefaultValue(pi.PropertyType, entityFieldValue))
                        insertsql.AddColumn(field.Value, entityFieldValue);
                }
            }
        } 

        private void UpdateFieldBind(UpdateSqlSection updatesql, T tag)
        {
            foreach (KeyValuePair<string, QueryColumn> field in Table.UpdateFields.Fields)
            {
                KeyValuePair<string, QueryColumn> field1 = field;
                PropertyInfo pi = Table.ValuePropertys.FirstOrDefault(item => item.Name.ToLower() == field1.Key.ToLower());
                if (pi != null)
                {
                    object entityFieldValue = pi.GetValue(tag, null);
                    //if (!CommonUtils.IsEqual(entityFieldValue, CommonUtils.DefaultValue(pi.PropertyType)))
                    if (!CommonUtils.IsDefaultValue(pi.PropertyType, entityFieldValue))
                        updatesql.AddColumn(field.Value, entityFieldValue);
                }
            }
            foreach (var queryColumn in Table.OptimisticLocking)
            {
                PropertyInfo pi = null;
                foreach (var item in Table.ValuePropertys)
                {
                    if (item.Name.ToLower() == queryColumn.Key.ToLower())
                    {
                        pi = item;
                        break;
                    }
                }
                if (pi != null)
                {
                    object entityFieldValue = pi.GetValue(tag, null);
                    if (!CommonUtils.IsDefaultValue(pi.PropertyType, entityFieldValue))
                        updatesql.Where(queryColumn.Value == entityFieldValue);
                }
            }
            updatesql.Where(Table.IdentyColumn == ReflectionUtils.GetFieldValue(tag, Table.IdentyFieldMemberInfo));
        }

        public InsertSqlSection GetInsertQuery(T tag)
        {
            InsertSqlSection insertsql = CreateInsert();
            InsertFieldBind(insertsql, tag); 
            return insertsql;
        }

        public UpdateSqlSection GetUpdateQuery(T tag)
        {
            var updatesql = CreateUpdate();
            UpdateFieldBind(updatesql, tag);
            return updatesql;
        }

        protected object GetPkValue(T tag)
        {
            return ReflectionUtils.GetFieldValue(tag, Table.IdentyFieldMemberInfo);
        }

        #endregion
 
        #region Cache Set Get

        //private LruDictionary<object, T> _cache;

        private bool _iscache = true;

        public bool IsCache
        {
            get { return _iscache; }
            set { _iscache = value; }
        }

        internal void AddCache(T tag)
        {
            if (_iscache)
            {
                base.AddCache(tag);
            }
        }

        internal void AddCache(IEnumerable<T> tags)
        {
            if (_iscache)
            {
                base.AddCache(tags);
            }
        }

        internal void RemoveCache(object value)
        {
            if (_iscache)
            {
                base.RemoveCache(typeof(T), value);
            }
        }

        internal void RemoveCache(object[] value)
        {
            if (_iscache)
            {
                base.RemoveCache(typeof(T), value);
            }
        }

        internal T GetCache(object key)
        {
            if (_iscache)
            {
                return base.GetCache(typeof(T), key) as T;
            }
            return default(T);
        }
         
        #endregion         

        #region Cache Double Get Set

        internal void RemoveCacheDouble(object value)
        {
              DBQueryFactory.RemoveCacheDouble(typeof (T), value);         
        }

        internal void RemoveCacheDouble(object[] value)
        {
            DBQueryFactory.RemoveCacheDouble(typeof(T), value);         
        }

        internal T GetCacheDouble(object key)
        {
            return DBQueryFactory.GetCacheDouble(typeof(T), key) as T;
        }

        protected void ClearCacheDouble()
        {
            DBQueryFactory.ClearCacheDouble(typeof(T));
        }
         
        #endregion        
    
        #region Common

        internal static QueryColumn GetQueryColumn(string p)
        {
            return DBQueryFactory.GetQueryColumn(Table, p);
            //if (Table.QueryColumns.ContainsKey(p.ToLower()))
            //    return Table.QueryColumns[p.ToLower()];
            //return null;
        }

        #endregion
    }
}
